﻿using System;
using System.Collections.Generic;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.DataServices;
using Petra.Gis.DataServices.Metadata;
using Petra.Gis.ReferenceSystemServices.Transformations;


namespace Petra.Gis.RenderingServices.PlanarBase 
{
  /// <summary>
  /// represents legend node that can holds spatial data to display in map
  /// this node supports only <c>SpatialDbCommand</c> data type with valid 
  /// <c>SpatialMetadata</c>
  /// </summary>
  public class DataLegendNode : LegendNode
  {

    protected SpatialDbCommand _data;




    public DataLegendNode(LegendNode parent, SpatialDbCommand data, string title)
      : base(parent, title)
    {
      _nodeType = LegendNodeType.DataNode;
      _data = data;
    }




    public override void RenderData()
    {
      // prepare spatial filter(s) for data
      // if spatialcommand refers to data with more than 1 spatial fiels
      // spatial filter must be defined for all spatial field
      if (this.Legend.Map.ApplyMapRangeAsSpatialFilter)
      {
        Dictionary<string, BoundingBox> spatialFilters = new Dictionary<string, BoundingBox>();
        foreach (GeometryFieldMetadata gfm in _data.Metadata.GeometryFieldsMetadata)
        {
          // prepare reverse transform for this geometry field coordinate system...
          SRSTransform t = this.Legend.Map.SRSTransformCache.GetTransform(gfm.SRS, true);
          spatialFilters.Add(gfm.Name, t.TransformBox(this.Legend.Map.Extent.Bounds));
        }
        _data.CoarseSpatialFilters = spatialFilters;
      }

      //todo:prepare renderers and styles for draw..


      // prepare SRSTransforms for each spatial field
      // as dictionary for fast search
      Dictionary<string, SRSTransform> spatialFields = new Dictionary<string, SRSTransform>();
      foreach (GeometryFieldMetadata gfm in _data.Metadata.GeometryFieldsMetadata)
      {
        // if spatial data domain is grid (Grid geometry) then prepare also reverse transform
        // into local cache
        // otherwise get appropriate (non reverse only) transform for each spatial field
        if (gfm.DomainType == DomainGeometryType.Grid)
          this.Legend.Map.SRSTransformCache.GetTransform(gfm.SRS, true);

        spatialFields.Add(gfm.Name, this.Legend.Map.SRSTransformCache.GetTransform(gfm.SRS, false));
      }

      //load data as DataReader
      SpatialDbDataReader dr = (SpatialDbDataReader)((SpatialDbCommand)_data).ExecuteReader();

      //step over records and paint into map...
      while (dr.Read())
      {
        foreach (KeyValuePair<string, SRSTransform> sft in spatialFields)
        {
          foreach (Renderer r in Renderers)
          {
            // for grid data dont do transform now, but attach transform 
            // and reverse transform into grid object and call RenderGeometry()
            if (((GeometryBase)dr.GetValue(dr.GetOrdinal(sft.Key))) is Grid)
            {
              ((Grid)dr.GetValue(dr.GetOrdinal(sft.Key))).Transform = sft.Value;
              //((Grid)dr.GetValue(dr.GetOrdinal(sft.Key))).InverseTransform = this.Legend.Map.SRSTransformCache.GetTransform(sft.Value.Source, true);

              if (sft.Value.IsInversible)
                ((Grid)dr.GetValue(dr.GetOrdinal(sft.Key))).InverseTransform = sft.Value.Inverse();
              else
                ((Grid)dr.GetValue(dr.GetOrdinal(sft.Key))).InverseTransform = this.Legend.Map.SRSTransformCache.GetTransform(sft.Value.Source, true);

              r.RenderGeometry(((Grid)dr.GetValue(dr.GetOrdinal(sft.Key))));
            }
            // otherwise do normal transform of vector geometry
            else
              r.RenderGeometry(sft.Value.Transform(((GeometryBase)dr.GetValue(dr.GetOrdinal(sft.Key)))));
          }
        }
      }


    }




    public SpatialDbCommand Data
    {
      get { return _data; }
    }


  }
}
